import tkinter as tk
import time
from tkinter.constants import HORIZONTAL, VERTICAL

# 2021-06-17 14:56 by 炸天帮成员
# 新增：以类的方式创建tkinter组件。
# 将生成2个tk.Tk()窗口，内部组件功能相同，且两个窗口互相独立。

class create_newFrame() :
    def __init__(self,root_ID) :
        '''
        作用：初始化 create_newFrame 类。
        参数：
            root_ID：用于区分是哪个tk.Tk()窗口。
        '''
        global window_0_geometry
        self.master_root_id = root_ID

        self.create_notepad(father_root=self.master_root_id,start_position=(200,0),geometry_size=(window_0_geometry[0]/2,window_0_geometry[1]/2))

        self.measure()

    def create_notepad(self,father_root,start_position=(0,0),geometry_size=(200,200)):
        '''
        作用：
            将创建一个
            组合型notepad组件（内部由tk.Widgets控件组合而成），这样以后每次调用该类的该函数时都将生成一个notepad框体。
        关联函数：
            self.create_notepad_bindings() -->用于组合型notepad组件中的tk.Text()控件的键盘事件绑定
            （由于键盘绑定事件函数的self特殊性会于自己写的create_newFrame类的self发生冲突，故需要新的self.func来包裹键盘绑定事件函数）
        组合型notepad组件的内部tk.Widgets控件结构：
            见__main__下的GUI结构注释（即组合型notepad组件就是一个tk.Frame()控件，该tk.Frame()控件中存在着tk.Label()、tk.Scrollbar()、tk.Text()、tk.Button()控件）。
        参数：
            father_root：
                即组合型notepad组件将生成在哪个 tk.Tk()窗口上。
            start_position：
                即组合型notepad组件的左上(x,y)坐标。
                （原理，所有的除了tk.Frame()控件都写在了tk.Frame()控件上，故只需要控制该tk.Frame()的坐标，即可移动整个组合型notepad组件）
            geometry_size：
                即组合型notepad组件的整体(width,height)大小。
                （原理，所有的除了tk.Frame()控件都写在了tk.Frame()控件上，故只需要控制该tk.Frame()的大小，即可控制整个组合型notepad组件的外形大小，当然内部组件的大小需要修改，暂未做完全的关联）
        '''
        self.frame_notepad_geometry = geometry_size
        self.frame_notepad = tk.Frame(father_root,bg = "cyan")
        self.frame_notepad.place(x=start_position[0],y=start_position[1],width=self.frame_notepad_geometry[0],height=self.frame_notepad_geometry[1])

        self.label_notepad = tk.Label(self.frame_notepad,text = "My NotePad",bg='#000000',fg='#ffffff') # 关于颜色的新写法
        self.label_notepad.place(x=0,y=0,width = self.frame_notepad_geometry[0])

        self.scroll_x_notepad = tk.Scrollbar(self.frame_notepad,orient=HORIZONTAL)
        self.scroll_y_notepad = tk.Scrollbar(self.frame_notepad,orient=VERTICAL)
        self.text_notepad = tk.Text(self.frame_notepad,wrap = tk.NONE,bg = "white",xscrollcommand=self.scroll_x_notepad.set,yscrollcommand=self.scroll_y_notepad.set)
        self.text_notepad.place(x=0,y=23,width = self.frame_notepad_geometry[0]-23+12,height = self.frame_notepad_geometry[1]-23*2+12)
        self.scroll_x_notepad.place(x=0,y=self.frame_notepad_geometry[1]-23+12,width = self.frame_notepad_geometry[0]-23+12,height=12)
        self.scroll_x_notepad.config(command = self.text_notepad.xview)
        self.scroll_y_notepad.place(x=self.frame_notepad_geometry[0]-23+12,y=23,width=12,height = self.frame_notepad_geometry[1]-23*2+12)
        #self.scroll_y_notepad.place(x=self.frame_notepad_geometry[0]-23+12,y=23,width=12,height = window_0_geometry[1]/2)
        self.scroll_y_notepad.config(command = self.text_notepad.yview)

        # 以下button用于清空notepad中的字
        self.button_clean_notepad = tk.Button(self.frame_notepad,bg = "cyan",text ='/',font=('Microsoft YaHei',10),command = lambda :self.text_notepad.delete('1.0','end'))
        self.button_clean_notepad.place(x=geometry_size[0]-12,y=geometry_size[1]-12,width=12,height=12)
        # 以下bindings用于绑定在notepad中的键盘事件
        self.create_notepad_bindings(father_root)

    def create_notepad_bindings(self,root_id):
        '''
        作用：绑定键盘事件“回车”至notepad的Text组件上，用于读取tk.Text()的文字内容。
        注意：“回车”在Tkinter中为"<Return>"
        特别：由于键盘绑定的事件的函数参数中必须带有self，这将会导致与create_newFrame类的self冲突，
              所以需要像这样子，在create_newFrame类中使用一个create_notepad_bindings(create_newFrame的self)函数中
              嵌套一个notepad_bind_func(键盘绑定的self)，此外由于在进入notepad_bind_func(self)前， frame_in_window_x 对象 是没有被创建完成的，
              所以关于 class对象 的选择，需要在 notepad_bind_func(self)函数中进行。否则，会报错。
        参数：
            root_id：用于区分是哪个tk.Tk()窗口，进而可以用来区分是哪个 frame_in_window_x 对象，
                    从而达到读取对应的tk.Tk()窗口下对应的 frame_in_window_x 对象中 的 tk.Text()组件文字内容。
        '''

        def notepad_bind_func(self):
            '''
            作用：
                用于self.text_notepad.bind()键盘绑定函数中的键盘事件响应。
            参数：
                该self不为 create_newFrame类 的self，为 self.text_notepad.bind()键盘绑定函数 所规定的self。
            '''
            global frame_in_window_0
            global frame_in_window_1

            def who_is_father_frame():
                '''
                    作用：用于通过tk.Tk()窗口，来区分是哪个 frame_in_window_x 对象。
                    返回值：
                        为一个元组  (father_frame,root_id_name)：
                            father_frame -> 即frame_in_window_x 对象，获取该值可进行 找到该对象下 的 tk.Text()组件。
                            root_id_name -> 用于print中提示当前为什么tk.Tk()窗口。
                '''
                if root_id == root0 :
                    father_frame = frame_in_window_0
                    root_id_name = "root0"
                elif root_id == root1 :
                    father_frame = frame_in_window_1
                    root_id_name = "root1"
                return (father_frame,root_id_name)
            
            father_frame_info = who_is_father_frame()
            # print(father_frame_info[0].notepad.get('1.0','end'))                  # 即从一行读取到最后一行
            print(father_frame_info[0].text_notepad.get('end - 1 lines','end'))    # 即从倒数第二行读取到最后一行
            time_now = time.ctime(time.time())
            imano_line_number = int(father_frame_info[0].text_notepad.index('end').split('.')[0])
            father_frame_info[0].text_notepad.insert('end + 2 lines',f'\n{father_frame_info[1]} now time is {time_now}.共有{imano_line_number}行。')
        self.text_notepad.bind('<Return>',notepad_bind_func)                        # 组合型notepad组件中tk.Text()控件所对应的键盘绑定函数，该函数对应的事件回调函数为notepad_bind_func(self)。

    def measure(self):
        '''
            用途：用于检测和显示tk.Widgets的大小。
            注意：在使用前必须要使用update()才行，否则返回结果为1，因为Tk()初始各个空间时，最小值为1.
        '''
        self.frame_notepad.update()
        print(f'{self.master_root_id} frame geometry = ',self.frame_notepad.winfo_geometry())   # 即组合型notepad组件（该组合的外壳就为一个tk.Frame()控件）的大小。
        self.label_notepad.update()
        print(f'{self.master_root_id} label_notepad geometry = ',self.label_notepad.winfo_geometry())
        self.text_notepad.update()
        print(f'{self.master_root_id} notepad geometry = ',self.text_notepad.winfo_geometry())

if __name__ == "__main__" :
    '''
        GUI结构：
        |____root0 -- tk.Tk()
        |       |__frame_in_window_0 -- class written by self
        |                   |__self.create_notepad() -- function
        |                            |__self.frame_notepad_geometry -- tk.Frame()
        |                                   |__self.label_notepad -- tk.Label()
        |                                   |__self.scroll_x_notepad -- tk.Scrollbar()
        |                                   |__self.scroll_y_notepad -- tk.Scrollbar()
        |                                   |__self.text_notepad -- tk.Text()    -->键盘事件绑定-->self.create_notepad_bindings()
        |                                   |                                                  |-->notepad_bind_func()
        |                                   |__self.button_clean_notepad -- tk.Button() -->按钮事件绑定-->lambda
        |
        |____root1 -- tk.Tk()
                |__frame_in_window_1 -- (结构同root0)
    '''
    root0 = tk.Tk()                                                  # 实例化1个Tk()窗口，记为root0。
    window_0_geometry = (400,400)                                    # 定义Tk()窗口的大小。
    root0.title("root0 window")                                      # 设定Tk()窗口名。
    root0.geometry(f'{window_0_geometry[0]}x{window_0_geometry[1]}') # 设定Tk()窗口大小。
    frame_in_window_0 = create_newFrame(root0)                       # 实例化create_newFrame对象，会在Tk()窗口中生成新的frame，即使用frame来充当无“最小化”“最大化”“消除”的窗口。

    root1=tk.Tk()                                                    # 实例化第2个Tk()窗口，记为root1。
    window_1_geometry = (400,200)
    root1.title("root1 window")
    root1.geometry(f'{window_1_geometry[0]}x{window_1_geometry[1]}')
    frame_in_window_1 = create_newFrame(root1)

    root0.mainloop()                                                 # 循环名为root0的Tk()窗口
    root1.mainloop()
